package com.zheinfo.common.utils;

import com.zheinfo.common.config.WechatConfig;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * 微信支付工具类
 * 实现微信支付统一下单接口调用及签名生成等功能
 */
@Component
public class WechatPayUtil {
    private static final Logger log = LoggerFactory.getLogger(WechatPayUtil.class);

    @Autowired
    private WechatConfig wechatConfig;

    /**
     * 微信支付统一下单接口URL
     */
    private static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    /**
     * 生成微信支付统一下单参数并调用接口
     * 
     * @param orderNo 订单号
     * @param totalFee 订单金额（单位：分）
     * @param body 商品描述
     * @param openId 用户openId
     * @param spbillCreateIp 终端IP
     * @return 统一下单返回结果
     */
    public Map<String, String> unifiedOrder(String orderNo, int totalFee, String body, 
                                          String openId, String spbillCreateIp) {
        try {
            // 检查配置是否有效
            if (wechatConfig.getAppid() == null || wechatConfig.getMchId() == null || 
                wechatConfig.getApiKey() == null || wechatConfig.getNotifyUrl() == null) {
                throw new RuntimeException("微信支付配置不完整，请检查appid、mchId、apiKey和notifyUrl配置");
            }
            
            log.info("开始微信支付统一下单 - 订单号: {}, 金额: {}分", orderNo, totalFee);
            
            // 构建统一下单参数
            Map<String, String> params = buildUnifiedOrderParams(orderNo, totalFee, body, openId, spbillCreateIp);
            
            // 生成签名
            String sign = generateSign(params);
            params.put("sign", sign);
            
            // 转换为XML
            String xmlParams = mapToXml(params);
            
            // 调用统一下单接口
            log.info("准备调用微信支付统一下单接口");
            String responseXml = doPost(UNIFIED_ORDER_URL, xmlParams);
            
            // 解析返回结果
            Map<String, String> result = xmlToMap(responseXml);
            
            // 验证返回结果
            if (result != null) {
                // 清理CDATA标签并获取状态码
                String returnCode = cleanCData(result.get("return_code"));
                String returnMsg = cleanCData(result.get("return_msg"));
                
                log.info("微信支付统一下单响应 - return_code: {}, return_msg: {}", returnCode, returnMsg);
                
                // 检查通信状态
                if ("SUCCESS".equals(returnCode)) {
                    String resultCode = cleanCData(result.get("result_code"));
                    if ("SUCCESS".equals(resultCode)) {
                        log.info("微信支付统一下单成功 - 订单号: {}", orderNo);
                        return result;
                    } else {
                        String errCode = cleanCData(result.get("err_code"));
                        String errCodeDes = cleanCData(result.get("err_code_des"));
                        log.error("微信支付统一下单失败: {}({})，订单号: {}", errCodeDes, errCode, orderNo);
                        
                        // 特殊处理appid和mch_id不匹配错误
                        if ("APPID_MCHID_NOT_MATCH".equals(errCode)) {
                            log.error("微信支付配置错误：appid({})和mch_id({})不匹配，请检查配置", 
                                     wechatConfig.getAppid(), wechatConfig.getMchId());
                        }
                        
                        throw new RuntimeException("微信支付统一下单失败: " + errCodeDes);
                    }
                } else if ("OK".equals(returnMsg) || "SUCCESS".equals(returnMsg)) {
                    // 特殊处理：当通信返回消息是OK或SUCCESS时，也认为是成功状态
                    log.info("微信支付统一下单返回OK/SUCCESS，尝试继续处理");
                    // 检查是否有业务结果码
                    String resultCode = cleanCData(result.get("result_code"));
                    if (resultCode == null || "SUCCESS".equals(resultCode)) {
                        return result;
                    }
                } else {
                    log.error("微信支付统一下单通信失败: {}，订单号: {}", returnMsg, orderNo);
                    throw new RuntimeException("微信支付统一下单通信失败: " + returnMsg);
                }
            }
            
            // 如果到这里，说明结果不符合预期格式
            log.error("微信支付统一下单返回结果格式错误，订单号: {}", orderNo);
            throw new RuntimeException("微信支付统一下单返回结果格式错误");
            
        } catch (RuntimeException e) {
            // 直接抛出RuntimeException，避免嵌套异常
            throw e;
        } catch (Exception e) {
            log.error("调用微信支付统一下单接口异常，订单号: {}", orderNo, e);
            // 为了便于调试，返回更详细的错误信息
            throw new RuntimeException("调用微信支付统一下单接口异常: " + e.getMessage());
        }
    }
    
    /**
     * 清理CDATA标签
     */
    private String cleanCData(String value) {
        if (value == null) {
            return null;
        }
        // 移除CDATA标签
        return value.replaceAll("<!\\[CDATA\\[|\\]\\]>", "").trim();
    }

    /**
     * 构建统一下单参数
     */
    private Map<String, String> buildUnifiedOrderParams(String orderNo, int totalFee, String body, 
                                                      String openId, String spbillCreateIp) {
        Map<String, String> params = new HashMap<>();
        // 使用配置中的appid和商户号
        params.put("appid", wechatConfig.getAppid());                          // 小程序ID
        params.put("mch_id", wechatConfig.getMchId());                        // 商户号
        params.put("nonce_str", generateNonceStr());                          // 随机字符串
        params.put("body", body);                                             // 商品描述
        params.put("out_trade_no", orderNo);                                  // 商户订单号
        params.put("total_fee", String.valueOf(totalFee));                    // 订单金额（分）
        params.put("spbill_create_ip", spbillCreateIp);                       // 终端IP
        params.put("notify_url", wechatConfig.getNotifyUrl());                // 通知地址
        params.put("trade_type", "JSAPI");                                  // 交易类型
        params.put("openid", openId);                                         // 用户openId（JSAPI必须）
        
        log.info("构建微信支付统一下单参数 - 商户号: {}, 订单号: {}", params.get("mch_id"), orderNo);
        return params;
    }

    /**
     * 生成支付签名（V3版本）
     * 用于前端调用支付接口的签名
     */
    public Map<String, String> generatePaySign(String prepayId, String timeStamp) {
        try {
            String appId = wechatConfig.getAppid();
            String nonceStr = generateNonceStr();
            String packageStr = "prepay_id=" + prepayId;
            String signType = "MD5";
            
            // 构建签名参数
            StringBuilder sb = new StringBuilder();
            sb.append("appId=").append(appId).append("&")
              .append("nonceStr=").append(nonceStr).append("&")
              .append("package=").append(packageStr).append("&")
              .append("signType=").append(signType).append("&")
              .append("timeStamp=").append(timeStamp).append("&")
              .append("key=").append(wechatConfig.getApiKey());
            
            // 生成MD5签名
            String paySign = md5(sb.toString()).toUpperCase();
            
            // 返回支付参数
            Map<String, String> payParams = new HashMap<>();
            payParams.put("timeStamp", timeStamp);
            payParams.put("nonceStr", nonceStr);
            payParams.put("package", packageStr);
            payParams.put("signType", signType);
            payParams.put("paySign", paySign);
            
            return payParams;
        } catch (Exception e) {
            log.error("生成支付签名异常", e);
            throw new RuntimeException("生成支付签名异常: " + e.getMessage());
        }
    }

    /**
     * 生成随机字符串
     */
    private String generateNonceStr() {
        return UUID.randomUUID().toString().replace("-", "").substring(0, 32);
    }

    /**
     * 生成签名
     */
    private String generateSign(Map<String, String> params) throws Exception {
        // 1. 参数名ASCII码从小到大排序
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        
        // 2. 拼接字符串
        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            String value = params.get(key);
            if (key != null && key.length() > 0 && value != null && value.length() > 0) {
                sb.append(key).append("=").append(value).append("&");
            }
        }
        
        // 3. 拼接API密钥
        sb.append("key=").append(wechatConfig.getApiKey());
        
        // 4. MD5加密
        return md5(sb.toString()).toUpperCase();
    }

    /**
     * MD5加密
     */
    private String md5(String str) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] bytes = md.digest(str.getBytes(StandardCharsets.UTF_8));
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(b & 0xFF);
            if (hex.length() == 1) {
                result.append("0");
            }
            result.append(hex);
        }
        return result.toString();
    }

    /**
     * 将Map转换为XML
     */
    private String mapToXml(Map<String, String> params) {
        StringBuilder xml = new StringBuilder();
        xml.append("<xml>");
        for (Map.Entry<String, String> entry : params.entrySet()) {
            xml.append("<").append(entry.getKey()).append(">").append(entry.getValue()).append("</").append(entry.getKey()).append(">");
        }
        xml.append("</xml>");
        return xml.toString();
    }

    /**
     * 将XML转换为Map
     */
    private Map<String, String> xmlToMap(String xml) throws Exception {
        Map<String, String> result = new HashMap<>();
        
        // 参数校验
        if (xml == null || xml.isEmpty()) {
            throw new IllegalArgumentException("XML content cannot be null or empty");
        }
        
        // 简单的XML解析，实际项目中可以使用更专业的XML解析库
        int xmlStart = xml.indexOf("<xml>");
        int xmlEnd = xml.indexOf("</xml>");
        
        // 检查是否包含有效的XML标签
        if (xmlStart == -1 || xmlEnd == -1 || xmlStart >= xmlEnd) {
            throw new IllegalArgumentException("Invalid XML format: missing or incorrect <xml> tags");
        }
        
        String content = xml.substring(xmlStart + 5, xmlEnd);
        
        String[] elements = content.split("</");
        for (String element : elements) {
            if (element != null && element.trim().length() > 0) {
                // 查找元素标签开始位置
                int tagStartPos = element.indexOf("<");
                if (tagStartPos >= 0) {
                    // 查找元素标签结束位置
                    int tagEndPos = element.indexOf(">", tagStartPos);
                    if (tagEndPos > tagStartPos) {
                        try {
                            // 提取标签名作为key
                            String key = element.substring(tagStartPos + 1, tagEndPos).trim();
                            
                            // 提取标签内容作为value
                            String value = element.substring(tagEndPos + 1).trim();
                            
                            if (!key.isEmpty()) {
                                result.put(key, value);
                            }
                        } catch (StringIndexOutOfBoundsException e) {
                            // 记录错误但继续处理其他元素
                            log.error("Error parsing XML element: {}", element, e);
                        }
                    }
                }
            }
        }
        
        return result;
    }

    /**
     * 发送POST请求
     */
    private String doPost(String url, String data) throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        
        // 设置请求头
        httpPost.setHeader("Content-Type", "text/xml");
        
        // 设置请求体
        StringEntity entity = new StringEntity(data, StandardCharsets.UTF_8);
        httpPost.setEntity(entity);
        
        // 发送请求
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                return EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
            }
            return null;
        } finally {
            response.close();
            httpClient.close();
        }
    }

    /**
     * 格式化订单金额（元转分）
     */
    public int formatAmount(double amount) {
        return (int) (amount * 100);
    }
}